#pragma once

#include "counter.h"
#include "debug_graphics.h"
#include "MessageRouter.h"
#include <map>
#include <vector>
#include <stdio.h>
#include <assert.h>
#define pixels
#include <algorithm>
#include <set>
#include <queue>

/**************************************************************************
 SCHEDULER 
***************************************************************************/



/*move these into constants*/
float pixels_in_a_meter = 16.0f pixels;
float meters_in_a_pixel = 1.0f/ pixels_in_a_meter;	//in meters
int entity_in_hash_counter = 0;
int global_id_counter = 0;




/**************************************************************************
 ENTITY 
***************************************************************************
	Has position and size in meters and world space
*/
struct  Entity2
{
	V2  _pos; 
	bool _tile;
	bool _active;
	int _id;
	V2 _vel;
	V2 _size;

//private:
	//Entity2(){}
};


struct DeltaPos
{
	V2 oldPos;
	V2 newPos;
};

std::queue<Entity2 *> entity_recycle_bin;

static void _entity_init(Entity2 * e)
{
	if (DEBUG_MODE){assert(global_id_counter < INT_MAX-1);}

	e->_tile = true;
	e->_id = global_id_counter++;
	e->_pos = v2(0,0);
	e->_vel = v2(0,0);
	e->_size = v2(0.0,0.0);
	e->_active = true;
}


struct EntityGrid;
struct GlobalEntityList;

/*
struct EntityCreatedData
{
	EntityGrid * grid;
	GlobalEntityList * all;
};
*/


struct EntityGrid;
struct EntityUpdateList;
struct GlobalEntityList;

struct Partitions
{
	EntityGrid * grid;
	EntityUpdateList * update_list;
	GlobalEntityList * global_list;
};


static Entity2 * entity_create(MessageRouter * router, Partitions * entitys)
{
	Entity2 * Result = 0;
	if (entity_recycle_bin.size()==0)
	{
		Result = new Entity2();
	}
	else
	{
		Result = entity_recycle_bin.front();
		entity_recycle_bin.pop();
		printf("recylced entity \n");
	}
	_entity_init(Result);

	/*EntityCreatedData * ecd = new EntityCreatedData();
	ecd->grid = grid;
	ecd->all = gel; */
	//MessageRouter_SpawnTriggeredMessage(router, Result, "entity_created", (void*)grid); 
	Shout(router, Result, "entity_created", entitys);
	//delete ecd;

	return Result;
}

/*
struct EntityRemovedData
{
	GlobalEntityList * all;
	EntityGrid * grid;
	EntityUpdateList * list;
};
*/

static void entity_remove(Entity2 * e, MessageRouter * router, Partitions * entitys, bool recycle)
{
	if (recycle){entity_recycle_bin.push(e);}

	e->_active = false;

	//EntityRemovedData * rd = new EntityRemovedData();
	//rd->grid = entitys->grid;
	//rd->list = entitys->update_list;
	//rd->all = entitys->global_list;

	//MessageRouter_SpawnTriggeredMessage(router, e, "entity_removed", (void*)rd); 
	Shout(router, e, "entity_removed", entitys);
	delete e;
	e = 0;

}

static void entity_set_pos(Entity2 * e, V2 pos);
static inline void entity_set_size(Entity2 * e, V2 size)
{
	e->_size = size;
}
static inline V2 entity_get_pos(Entity2 * e){return e->_pos;}
static inline V2 entity_get_size(Entity2 * e){return e->_size;}


static inline Rect entity_get_bounds(Entity2 * e)
{
	V2 pos = entity_get_pos(e);
	V2 size = entity_get_size(e);
	Rect Result = {pos.x, pos.y, pos.x + size.x, pos.y + size.y}; 
	return Result;
}

/**************************************************************************
 ENTITY PARTITION
**************************************************************************
	_entitys contains a list of entitys in this partition
	_set contains the entitys unique ids
*/
const float partition_size = 1;	//in meters

struct  EntityPartition
{
	std::set<int> _set;
	std::vector<Entity2*> _entitys;
};

/*
static EntityPartition * entity_partition_create(){
	EntityPartition * ep = new EntityPartition();
	ep->_entitys.reserve(8);
	return ep;
}
*/

// does this want to return a pointer instead?
static inline std::vector<Entity2*> entity_partition_get_all(EntityPartition * ep){
	return ep->_entitys;
}

//adds entity to partition
static void entity_partition_add(EntityPartition * ep, Entity2 * e){

	if (ep->_set.find(e->_id) == ep->_set.end()){
		entity_in_hash_counter++;
		ep->_entitys.push_back(e);
		ep->_set.insert(e->_id);
	}
}
	
//clears the entity partition
static inline void entity_partition_clear(EntityPartition * ep)	{
	ep->_entitys.clear();
	ep->_set.clear();
}

//remove entity from partition
static void entity_partition_remove(EntityPartition * ep, Entity2 * e){

	if (ep->_set.find(e->_id) ==ep->_set.end())	{
		return;
	}
	ep->_set.erase(e->_id);

	std::vector<Entity2*>::const_iterator iter;
	int index =0;

	for (iter = ep->_entitys.cbegin(); iter != ep->_entitys.end(); iter++){
		if ((*iter)->_id == e->_id){
			break;
		}
		index++;
	}
		
	if(iter != ep->_entitys.end()){
		ep->_entitys.erase(ep->_entitys.cbegin() + index);
		entity_in_hash_counter--;
	}
}

static inline bool entity_partition_contains_entity_with_id(EntityPartition * ep, int id)
{
	//is checking map quicker?
	std::vector<Entity2*>::const_iterator i =ep->_entitys.cbegin();
	int index =0;
	for (i; i != ep->_entitys.end(); i++)
	{
		if ((*i)->_id == id)
		{
			return true;
		}
		index++;
	}
	return false;
}

static inline int entity_partition_get_count(EntityPartition * ep){
	return ep->_entitys.size();
}

/////////////////////////////////////////////////
//Global Entity List

struct GlobalEntityList
{
	EntityPartition list;
};

void global_entity_list__Add(GlobalEntityList * gle, Entity2 * e)
{
	entity_partition_add(&gle->list,e);

}

void global_entity_list__Remove(GlobalEntityList * gle, Entity2 * e)
{
	entity_partition_remove(&gle->list,e);
}


std::vector<Entity2*> global_entity_list__Get(GlobalEntityList * gle)
{
	return gle->list._entitys;
}

static MessagePacket * global_entity_list__on_removed(MessagePacket * mp)
{
	printf("removed entity from global list\n");
	Partitions * entitys = (Partitions*)mp->data;

	global_entity_list__Remove(entitys->global_list,(Entity2*)mp->sender);
	return 0;
}


static MessagePacket * global_entity_list__on_created(MessagePacket * mp)
{
	printf("added entity to global list\n");
	Partitions * entitys= (Partitions*)mp->data;
	GlobalEntityList * gel = entitys->global_list;
	global_entity_list__Add(gel,(Entity2*)mp->sender);
	return 0;
}

/**************************************************************************
 ENTITY GRID
**************************************************************************

/*
each partition x,y index has a EntityPartition* that contains a list of
entitys that intersect that grid

origin is at world (0,0)
each grid index is world distance "partition_size" away from the next index
*/

struct  EntityGrid
{
	//<XIndex,YIndex>
	std::map<pair<int,int>,EntityPartition*> partition_map;
};

typedef std::multimap<pair<int,int>,Entity2 *>::iterator map_iter;


static void entity_grid_add(EntityGrid * epg,Entity2 * e)
{
	//loop over grid vertices within entity rect
	Recti grid_index_bounds =  math_rect_integer_divide(partition_size,entity_get_bounds(e));

	for (int xid = grid_index_bounds.min.x; xid <= grid_index_bounds.max.x; xid++)
	{
		for (int yid = grid_index_bounds.min.y; yid <= grid_index_bounds.max.y; yid++)
		{
			pair<int,int> index (xid,yid);
			if(!epg->partition_map[index]){epg->partition_map[index]= new EntityPartition();}
				
			//partition_map[index]->add(e);
			entity_partition_add(epg->partition_map[index], e);
		}
	}

}

/*
	using this inset indicates that there is a problem!!
*/
static void  entity_grid_remove(EntityGrid * epg, Entity2 * e, V2 old_world_pos)
{
	Recti grid_index_bounds = math_rect_integer_divide(partition_size,rect_centre(old_world_pos,entity_get_size(e)));

	for (int xid = grid_index_bounds.min.x; xid <= grid_index_bounds.max.x; xid++)
	{
		for (int yid = grid_index_bounds.min.y; yid <= grid_index_bounds.max.y; yid++)
		{
			pair<int,int> index(xid,yid);
			if(epg->partition_map[index])
			{
				//partition_map[index]->remove(e);
				entity_partition_remove(epg->partition_map[index], e);
			}
		}
	}

}

static std::vector<Entity2*>  entity_grid_get_entitys_in_partition(EntityGrid * epg, V2 world_pos)
{
	pair<int,int> index ((int)(world_pos.x / partition_size), (int)(world_pos.y/partition_size));
	if(!epg->partition_map[index]){epg->partition_map[index]= new EntityPartition();}
	return entity_partition_get_all(epg->partition_map[index]);
	//partition_map[index]->get_all();
}

static std::vector<Entity2 *>  entity_grid_get_entitys_in_rect(EntityGrid * epg, Rect rect)
{
	std::vector<Entity2 *> ResultWithDuplicates;

	Recti grid_index_bounds = math_rect_integer_divide(partition_size,rect);

	for (int xid = grid_index_bounds.min.x; xid <= grid_index_bounds.max.x; xid++)
	{
		for (int yid = grid_index_bounds.min.y; yid <= grid_index_bounds.max.y; yid++)
		{
			pair<int,int> index(xid,yid);
			if(!epg->partition_map[index]){epg->partition_map[index]= new EntityPartition();}
				
			std::vector<Entity2 *> ret = entity_partition_get_all(epg->partition_map[index]);//partition_map[index]->get_all();
			ResultWithDuplicates.insert(ResultWithDuplicates.end(),ret.begin(), ret.end());
		}
	}
		

	std::vector<Entity2 * > Result;
	std::vector<Entity2 *>::iterator iter;
	std::set<int> found_set;

	for (iter = ResultWithDuplicates.begin() ; iter!= ResultWithDuplicates.end(); iter++)
	{
		int id = (*iter)->_id;

		//key not filled yet
		if (found_set.find(id) == found_set.end())
		{
			found_set.insert(id);
			Result.push_back(*iter);
		}
	}
	return Result;

}

	
static std::vector<Entity2 *>  entity_grid_get_entitys_in_rect_exclude(EntityGrid * epg, Rect region, Rect exclude_region)
{
	std::vector<Entity2 *> ResultWithDuplicates;

	Recti grid_index_bounds = math_rect_integer_divide(partition_size,region);

	for (int xid = grid_index_bounds.min.x; xid <= grid_index_bounds.max.x; xid++)
	{
		for (int yid = grid_index_bounds.min.y; yid <= grid_index_bounds.max.y; yid++)
		{
			//should this be a overlap comparison or just point??
			if (math_point_in_rect(v2((float)xid * partition_size, (float)yid* partition_size),exclude_region))
			{
				continue;
			}
			pair<int,int> index(xid,yid);
			if(!epg->partition_map[index]){epg->partition_map[index]= new EntityPartition();}
			//std::vector<Entity2 *> ret = partition_map[index]->get_all();
			std::vector<Entity2 *> ret = entity_partition_get_all(epg->partition_map[index]);
			ResultWithDuplicates.insert(ResultWithDuplicates.end(),ret.begin(), ret.end());
		}
	}
		

	std::vector<Entity2 * > Result;
	std::vector<Entity2 *>::iterator iter;
	std::set<int> found_set;

	for (iter = ResultWithDuplicates.begin() ; iter!= ResultWithDuplicates.end(); iter++)
	{
		int id = (*iter)->_id;

		//key not filled yet
		if (found_set.find(id) == found_set.end())
		{
			found_set.insert(id);
			Result.push_back(*iter);
		}
	}
	return Result;

}

static MessagePacket * entity_grid_on_removed(MessagePacket * mp)
{
	void * grid = 0;
	Partitions * entitys = (Partitions*)mp->data;
	
	entity_grid_remove(entitys->grid, (Entity2*)mp->sender, ((Entity2*)mp->sender)->_pos);
	return 0;
}

//static MessagePacket * entity_grid_on_created(MessagePacket * mp)
//{
//	void * grid = 0;
//	
//	EntityCreatedData * ecd = (EntityCreatedData*)mp->data;
//	
//	entity_grid_add(ecd->grid, (Entity2*)mp->sender);
//	return 0;
//}

/**************************************************************************
VIEWPORT
***************************************************************************/

struct Viewport
{	
	V2 pos; //world pos meters 
};

//returns top left world point and bot right point in viewport
static Rect viewport_get_world_extents(Viewport * vp)
{
	Rect Result = {vp->pos.x, vp->pos.y, 
		vp->pos.x + (float)G::SCREEN_WIDTH * meters_in_a_pixel,
		vp->pos.y + (float)G::SCREEN_HEIGHT* meters_in_a_pixel};
	return Result;
}

static inline V2 vieport_to_screen_scale(V2 world_bounds)
{
	return world_bounds * pixels_in_a_meter;
}

static inline V2 viewport_to_world_space(V2 screen_space, Viewport  * v)
{
	return v->pos + (screen_space * meters_in_a_pixel);
}

static inline V2 viewport_to_world_scale(V2 screen_bounds)
{
	return screen_bounds * meters_in_a_pixel;
}

static inline V2 viewport_to_screen_space(V2 world_space, Viewport * v)
{
	V2 meters_from_camera_origin = world_space - v->pos;
	V2 pixels_from_camera_origin = meters_from_camera_origin * pixels_in_a_meter;
	return pixels_from_camera_origin;
}

static inline Rect viewport_to_world(Rect screen_bounds, Viewport * v)
{
	Rect Result = {v->pos + (screen_bounds.min * meters_in_a_pixel),
		v->pos + (screen_bounds.max * meters_in_a_pixel)};
	return Result;
}

static inline Rect viewport_to_screen(Rect world_bounds, Viewport * v)
{
	Rect Result = {(world_bounds.min - v->pos)*pixels_in_a_meter,
		(world_bounds.max - v->pos)*pixels_in_a_meter};
	return Result;
}


/**************************************************************************
ENTITY UPDATE LIST
***************************************************************************/

struct EntityUpdateList
{
	Rect _world_bounds;
	EntityPartition _entitys;
};


static EntityUpdateList * entity_update_list_create()
{
	EntityUpdateList * Result = new EntityUpdateList();
	Result->_world_bounds = rect_centre(v2(0,0),
		v2((float)G::SCREEN_WIDTH, (float)G::SCREEN_HEIGHT) * meters_in_a_pixel);
}

//probably a more elegant way to check if we need to add / remove
//returns false if entity was rejected from update list,
static bool entity_update_list_update_entity_check(EntityUpdateList * eul, Entity2 * e)
{
	bool new_col = math_rect_overlap(eul->_world_bounds,entity_get_bounds(e));

	if (!new_col){
		//_entitys->remove(e);
		entity_partition_remove(&eul->_entitys, e);
		return false;
	}

	if(new_col){
		//_entitys->add(e);
		entity_partition_add(&eul->_entitys, e);
		return true;
	}
	return false;
}

static void entity_update_list_move_update_region(EntityUpdateList * eul, 
													Viewport * viewp, EntityGrid * egrid)
{
	Rect old_bounds = eul->_world_bounds;
	eul->_world_bounds = viewport_get_world_extents(viewp);

	//we probs need to check what new entitys we need to add...
	vector<Entity2*> to_add = entity_grid_get_entitys_in_rect_exclude(egrid,
		eul->_world_bounds, old_bounds);

	vector<Entity2*>::iterator iter;
	for (iter = to_add.begin(); iter != to_add.end(); ++iter)
	{
		entity_update_list_update_entity_check(eul,*iter);
	}

	//remove all that aren't inside anymore... 
	vector<Entity2*> ents = entity_partition_get_all(&eul->_entitys);//_entitys->get_all();
	entity_partition_clear(&eul->_entitys);

	for (iter = ents.begin(); iter != ents.end(); ++iter)
	{
		bool entity_accepted= entity_update_list_update_entity_check(eul,*iter);
		
	}
}

static std::vector<Entity2 * > entity_update_list_get_entitys(EntityUpdateList * eul)
{
	return entity_partition_get_all(&eul->_entitys);
	//return _entitys->get_all();
}

static MessagePacket * entity_update_list_on_removed(MessagePacket * mp)
{
	Partitions * entitys = (Partitions*)mp->data;

	//MD_Get(&md, "entity_update_list", eul);
	entity_partition_remove(&entitys->update_list->_entitys, (Entity2*)mp->sender);
	return 0;
}

/**************************************************************************
GAME 2
***************************************************************************/
/*
struct Partitions
{
	EntityGrid grid;
	EntityUpdateList update_list;
	GlobalEntityList global_list;
};
*/

struct Game2
{
	Viewport * _viewport;
	MessageRouter *	_router;
	Partitions * _entitys;
	CounterData * _counter;
	//private:
	//Game2(){};
};

Game2 * _game_;

static inline Game2 * game_get()
{
	if (_game_)	{return _game_;}

	_game_ = new Game2();
	_game_->_viewport = new Viewport();
	_game_->_entitys = new Partitions();
	_game_->_entitys->global_list = new GlobalEntityList();
	_game_->_entitys->update_list = new EntityUpdateList();
	_game_->_entitys->grid = new EntityGrid();
	_game_->_counter = new CounterData();
	_game_->_router = new MessageRouter();

	return _game_;
}

static inline void game_end()
{
	if (_game_)
	{
		_game_->_entitys->global_list->list._entitys.clear();
		_game_->_entitys->global_list->list._set.clear();

		printf("game delete\n");
		delete _game_->_router;
		delete _game_->_counter;
		delete _game_->_entitys->update_list;
		delete _game_->_entitys->global_list;
		_game_->_entitys->update_list = 0;
		_game_->_entitys->global_list = 0;
		delete _game_->_entitys;
		delete _game_->_viewport;
		delete _game_;

		_game_->_router = 0;
		_game_->_counter = 0;
		_game_->_entitys = 0;
		_game_->_viewport = 0;
		_game_ = 0;
	}
}



static MessagePacket * entity_move_listener(MessagePacket * packet)//void * sender, std::string message, MessageData md)	
{
	Entity2 * e = (Entity2 * )packet->sender;
	DeltaPos * dp = (DeltaPos*)packet->data;


	//make sure we actually moved!//
	if (DEBUG_MODE){assert(dp->oldPos.x != dp->newPos.x || dp->oldPos.y != dp->newPos.y);}
	if (DEBUG_MODE){assert(dp->newPos.x == entity_get_pos(e).x || dp->newPos.y ==entity_get_pos(e).y);}

	Game2 * game = game_get();
	entity_grid_remove(game->_entitys->grid, e, dp->oldPos);
	entity_grid_add(game->_entitys->grid, e);
	bool entity_accepted = entity_update_list_update_entity_check(game->_entitys->update_list, e);
	if (!entity_accepted)
	{
		Shout(packet->router,e,"entity_removed_from_update_list");
	}
	return 0;
}

//returns the mouses viewport position
inline V2 game_get_mouse_pos()
{
	int mx = 0; int my = 0;
	SDL_GetMouseState(&mx, &my);
	V2 mouse_pos = v2(mx - G::GRAPHICS_RENDERWINDOW_TOP_LEFT.x, 
	my - G::GRAPHICS_RENDERWINDOW_TOP_LEFT.y);
	return mouse_pos;
}

Entity2 * temp_create_new_entity(MessageRouter * router, Partitions * entitys, V2 pos, V2 size)
{
	Entity2 * e = entity_create(router, entitys);// new Entity2();

	entity_set_pos(e, pos);
	e->_size = size;
	DeltaPos * dp = new DeltaPos();
	dp->newPos = pos;
	dp->oldPos = v2(-0.01f,0.001f);
	//MessageRouter_SpawnTriggeredMessage(router,e, "on_move",dp);
	Shout(router, e, "on_move", dp);
	delete dp;
	return e;
}

//one to one mapping eventname<->router_callback
//struct GameCallbackMap
//{
//	std::vector<pair<string, MessageCallbackFnPtr>> name_and_callback_ptr;
//};

#if 0
bool _game_impartial_setup(Game2 * game)
{
	MessageRouter *	router;
	_game_ = game;
	if(game->_router)
	{
		router = game->_router;
	}else
	{
		router = new MessageRouter();
	}


	/* Should this go here? or back into main? */
	vector<pair<string, MessageCallbackFnPtr> > callback_map;
	callback_map.push_back(pair<string,MessageCallbackFnPtr>("on_move", entity_move_listener));
	callback_map.push_back(pair<string,MessageCallbackFnPtr>("entity_removed", entity_grid_on_removed));
	callback_map.push_back(pair<string,MessageCallbackFnPtr>("entity_removed", entity_update_list_on_removed));

	//global_entity_list__on_created
	callback_map.push_back(pair<string,MessageCallbackFnPtr>("entity_created",global_entity_list__on_created));
	callback_map.push_back(pair<string,MessageCallbackFnPtr>("entity_removed",global_entity_list__on_removed));

	for (int map_index = 0; map_index < callback_map.size(); map_index++)
	{
		MessageRouter_RegisterTriggerCallback(router,
			callback_map.at(map_index).second,		//callback ptr function
			callback_map.at(map_index).first);		//callback string name
	}


	assert(game->_entitys);
	for (int i = 0; i < 40; i++	)//50
	{
			V2 spacing = {-2+0.02f*(float)(rand()%1000),-2+0.02f*(float)(rand()%1000)};
			V2 pos = {spacing.x,spacing.y};

			temp_create_new_entity(router,game->_entitys,pos,v2(2,2));
	}
	
	
	game->_viewport->pos = v2(-5,-5);
	//entity_update_list_move_update_region(game->_entity_update_list, game->_viewport, game->_entity_grid);

	return true;
}
#endif

void update_entity(Entity2 * e, float dt)
{
	float ticks = (SDL_GetTicks() * (float)e->_id * 0.01f) / 1000.0f;
	//e->_vel.x = 0.0001f;//;sin(ticks/5);
	//e->_vel.y = 0.0001f;//.01f*cos(ticks/5);
	//if (e->_vel.x <0 ){e->_vel.x += dt; }
	//else{e->_vel.x -= dt; }

	//if (e->_vel.y <0 ){e->_vel.y += dt; }
	//else{e->_vel.y -= dt; }
	float mod_dt = - (1.25f*dt*dt);
	e->_vel.x *= pow(0.1f,dt + mod_dt );
	e->_vel.y *= pow(0.1f,dt + mod_dt);

	//gravity
	e->_vel.y -= 10.0f * (dt + 2*mod_dt);

	entity_set_pos( e,entity_get_pos(e)+ (e->_vel * dt));
}

void entity_set_pos(Entity2 * e, V2 new_pos)
{
	V2 posv = entity_get_pos(e);
	if (new_pos.x == posv.x && new_pos.y == posv.y){return;}

	V2 old_pos = posv;
	e->_pos.x = new_pos.x;
	e->_pos.y = new_pos.y;
		
	// make call saying we've moved to a new location
	DeltaPos * dp = new DeltaPos();

	dp->newPos = e->_pos;
	dp->oldPos = old_pos;
	//MessageRouter_SpawnTriggeredMessage(game_get()->_router,e, "on_move",dp);
	Shout(game_get()->_router,e,"on_move",dp);
	delete dp;
}


//should return all entitys in world region
typedef std::vector<Entity2*> (*EntityRegionCallback)(Game2 * game, Rect game_bounds);

//should return all simulated entitys, e.g. all entitys that we want to update this frame
typedef std::vector<Entity2*> (*EntitySimulatedCallback)(Game2 * game);

//should return all active entitys
typedef std::vector<Entity2*> (*EntityActiveCallback)(Game2 * game);

typedef void (*UpdateSimListCallback)(Game2 * game);

struct GamePartitionMethods
{
	EntityRegionCallback region_callback;
	EntitySimulatedCallback simulated_callback;
	EntityActiveCallback active_callback;

	UpdateSimListCallback update_sim_list_from_viewport;
};




///////////////////////
//game partition glue//
/*
	Bridge between game and partition classes
*/
std::vector<Entity2 *> glue_get_ents_in_region(Game2 * game, Rect game_bounds)
{
	std::vector<Entity2*> Result = entity_grid_get_entitys_in_rect(game->_entitys->grid, game_bounds);
	return Result;
}
std::vector<Entity2 *> glue_get_simulated_ents(Game2 * game)
{
	std::vector<Entity2*> Result = entity_update_list_get_entitys(game->_entitys->update_list);
	return Result;
}

 void glue_update_sim_list(Game2 * game)
 {
	 entity_update_list_move_update_region(game->_entitys->update_list,game->_viewport,game->_entitys->grid);
 }

//TODO: Add GlobalEntityList
std::vector<Entity2 *> glue_get_global_ents(Game2 * game)
{
	std::vector<Entity2*> Result = global_entity_list__Get(game->_entitys->global_list);
	return Result;
}

//When an entity enter a region of the game world, a message is fired

//////////////////////
//TriggerRect    	//
/*
	Maybe try to copy entity collision

	EnterRect
	LeaveRect
	InRect
*/
struct EntityTriggerRectList
{
	Rect extents;
	vector<pair<Rect,string> > rect_and_name;
};

void on_entity_moved(Entity2 * e, Rect e_new_bounds, Rect e_old_bounds)
{
	//if entity in extents
}



typedef void (*GGVoidFnPtr)();
typedef void (*GGRectColor)(Rect bounds, int r, int g, int b, int a);

struct GameGraphicMethods
{
	GGRectColor AddRectFillSS;
	GGVoidFnPtr Begin;
	GGVoidFnPtr End;
	GGVoidFnPtr Render;
};


///////////////////////////////////////////
//GAME									 //
/*
	Do we want to separate graphics methods with a GraphicMethods struct?
	TODO: fix drawing in screen space... this shouldn't be handled here
*/
#if 0
bool removed = false;
bool _game_impartial_run(Game2 * game, float dt, GamePartitionMethods methods, GameGraphicMethods draw )
{
	//Shout(game->_router, game, "game_update"); 

	//render rect around viewport
	Rect viewport_bounds = viewport_get_world_extents(game->_viewport);
	Rect screen_viewport_bounds = viewport_to_screen(viewport_bounds, game->_viewport);

	draw.AddRectFillSS(screen_viewport_bounds,64,0,64,72);

	game->_viewport->pos.x += (SDL_GetTicks() < 10000 ? 0.05f : -0.05f) * (1+sin(SDL_GetTicks() / 100.0f));
	
	methods.update_sim_list_from_viewport(game);


	std::vector<Entity2*> update_entitys = methods.simulated_callback(game); //active_callback(game);

	if (SDL_GetTicks() > 1000 * 4 && removed == 0){
		std::vector<Entity2*>::iterator remove_ent_iter;
		int dead_count = 0;
		removed = 1;
		for (remove_ent_iter = update_entitys.begin();dead_count < 20 && remove_ent_iter != update_entitys.end(); remove_ent_iter++)	{
			
			entity_remove(*remove_ent_iter, game->_router,game->_entitys);
			dead_count++;
		}
	}
	draw.Begin();
	std::vector<Entity2*>::iterator ent_update_iter;
	for (	ent_update_iter = update_entitys.begin();
			ent_update_iter != update_entitys.end();
			ent_update_iter++)
	{
		V2 ent_pos = entity_get_pos(*ent_update_iter);
		V2 ent_size = entity_get_size(*ent_update_iter);

		/*
			Think about proper collision!!
			E.g. EnterCollision
			LeaveCollision
			InCollision

			Mud:
				enter - change animation
				during - change move speed
				leave - change animation

		*/
		bool collides = false;
		bool found_self = false;
		bool active = (*ent_update_iter)->_active;	

		if (active)	{
			std::vector<Entity2*> collision_entiys = methods.region_callback(game,rect_centre(ent_pos,ent_size));

			std::vector<Entity2*>::iterator collision_ent_iter;
			for (collision_ent_iter = collision_entiys.begin();
				 collision_ent_iter != collision_entiys.end();
				 collision_ent_iter++)
			{
				V2 c_pos = entity_get_pos(*collision_ent_iter);
				V2 c_size = entity_get_size(*collision_ent_iter);

				if ((*ent_update_iter)->_id == (*collision_ent_iter)->_id){
					found_self = true;
					continue;
				}
				if (math_rect_overlap(c_pos, c_pos + c_size, ent_pos, ent_pos + ent_size)){
					collides = true;
				}
			}
		}

		V2 ent_screen_point = viewport_to_screen_space(ent_pos,game->_viewport);
		Rect ent_bounds = {ent_screen_point,ent_screen_point +  vieport_to_screen_scale(ent_size)};
		draw.AddRectFillSS(ent_bounds,120,collides?50:250,active?50:250,255);

		if(active)
		{
			update_entity((*ent_update_iter),dt);
		}
	}

	SDL_Event event;
	while(SDL_PollEvent(&event))
	{

	}

	draw.Render();
	draw.End();

	return false;
}
#endif

//////////////////////

/*

int removed  = 0;
bool game_run(Game2 * game, float last_dt){
	Rect viewport_bounds = viewport_get_world_extents(game->_viewport);//game->_viewport->get_world_extents();

	entity_update_list_move_update_region(game->_entity_update_list,
		game->_viewport, game->_entity_grid);

	std::vector<Entity2*> ents =	entity_update_list_get_entitys(game->_entity_update_list);

	SDL_Color col = {0,0,0,255};
	debug_graphics_start_render();

	Rect screen_viewport_bounds = viewport_to_screen(viewport_bounds, game->_viewport);
	debug_graphics_add_rect_to_stack_ss(screen_viewport_bounds.min,screen_viewport_bounds.max - screen_viewport_bounds.min,64,0,64,72);

	std::vector<Entity2*>::iterator iter;
	
	if (SDL_GetTicks() > 1000 * 4 && removed == 0){
		int dead_count = 0;
		removed = 1;
		for (iter = ents.begin();dead_count < 20 && iter != ents.end(); iter++)	{
			entity_remove(*iter, game->_router, game->_entity_grid, game->_entity_update_list);
			dead_count++;
		}

		for (int i = 0; i < 40; i++	)//50
		{
			V2 spacing = {-2+0.02f*(float)(rand()%1000),-2+0.02f*(float)(rand()%1000)};
			V2 pos = {spacing.x,spacing.y};

			temp_create_new_entity(game->_router,game->_entity_grid,pos,v2(2,2));
		}
	}
	

	for (iter = ents.begin();iter != ents.end(); iter++){
		V2 i_pos = entity_get_pos(*iter);
		V2 i_size = entity_get_size(*iter);

		V2 i_screen_point = viewport_to_screen_space(i_pos,game->_viewport);
		V2 i_screen_span = vieport_to_screen_scale(i_size);

		bool in_expected_partition = false;

		bool collides = false;
		bool found_self = false;
		bool active = (*iter)->_active;

		if (active)	{
			std::vector<Entity2*> collide_ents =entity_grid_get_entitys_in_rect(game->_entity_grid, 
					rect_centre(i_pos,i_size));

			std::vector<Entity2*>::iterator col_iter;

			for (col_iter = collide_ents.begin(); col_iter != collide_ents.end(); col_iter++)
			{
				V2 c_pos = entity_get_pos(*col_iter);
				V2 c_size = entity_get_size(*col_iter);

				if ((*iter)->_id == (*col_iter)->_id)				{
					found_self = true;
					continue;
				}
				if (math_rect_overlap(c_pos, c_pos + c_size, i_pos, i_pos + i_size))				{
					collides = true;
				}
			}
		}
		debug_graphics_add_rect_to_stack_ss(i_screen_point,i_screen_span,
			120,collides?50:250,active?50:250,255);

		if(active)
		update_entity((*iter),last_dt);

	}
	
	//draw partition grid

	Recti grid_index_bounds = math_rect_integer_divide(partition_size,viewport_bounds);

	for (int xid = grid_index_bounds.min.x-1; xid <= grid_index_bounds.max.x; xid++)
	{
		for (int yid = grid_index_bounds.min.y-1; yid <= grid_index_bounds.max.y; yid++)
		{
			V2 screen_point = viewport_to_screen_space(v2(xid *partition_size, yid*partition_size) ,game->_viewport);
			V2 screen_span = vieport_to_screen_scale(v2(partition_size));
			debug_graphics_render_rect_outline_on_screen(col,screen_point,screen_span);
		}
	}

	//show entitys in update list

	vector<Entity2 *> to_update = entity_update_list_get_entitys(game->_entity_update_list);
	for (iter = to_update.begin();iter != to_update.end(); iter++)
	{
		V2 i_pos = entity_get_pos(*iter);
		V2 i_size = entity_get_size(*iter);

		V2 i_screen_point = viewport_to_screen_space(i_pos,game->_viewport);
		V2 i_screen_span = vieport_to_screen_scale(i_size);

		debug_graphics_add_rect_to_stack_ss(i_screen_point,i_screen_span*0.5f,0,0,0,255);
	}


	SDL_Event event;
	while(SDL_PollEvent(&event))
	{

	}

	debug_graphics_render_stack();
	debug_graphics_final_render();


	//SDL_Delay(5);
	return false;
}
*/